/****************************************************************

kernel/video.c
Text-mode video driver for AWOS

Copyright (c) 2006 Andrew Wilcox. All rights reserved.

Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.

3. The end-user documentation included with the redistribution,
if any, must include the following acknowledgment:

"This product includes software developed by Andrew Wilcox for
AWOS. (http://code.google.com/p/awos)."

Alternately, this acknowledgment may appear in the software
itself, if and wherever such third-party acknowledgments normally
appear.

4. The names "AWOS" and "Andrew Wilcox" must not be used to
endorse or promote products derived from this software without
prior written permission. For written permission, please contact
Andrew.R.Wilcox@Gmail.com.

5. Products derived from this software may not be called "AWOS"
 nor may "AWOS" appear in their name, without prior written
 permission of Andrew Wilcox.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ANDREW WILCOX BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

****************************************************************/

/*! \file video.c
 * video functions
 */

#include <asm.h>
#include <kernel.h>
#include <pf.h>
#include <string.h>

/**pointer to VGA memory -- usually 0xB8000**/
unsigned char *vgamem;
/**position of cursor on x plane**/
int x = 0;
/**position of cursor on y plane**/
int y = 0;
/**current color byte (e.g 0x07 for white on black)**/
unsigned char color;
extern BOOL debug;
extern BOOL log;

/*! \fn void clrscr()
 * \brief Clears the screen
 * \return Nothing
 */
void clrscr()
{
	int i;
	int blank;
	
	blank = 0x20 | (0x0F << 8);
	
	for(i = 0; i < 25; i++)
		memsetw(vgamem + i * 80, blank, 80);
	
	x = 0;
	y = 0;
	KiVgaUpdateCursor();
	
	return;
}

/*! \fn void KiVgaUpdateCursor() 
 * \brief updates the position of the hardware cursor
 * \return Nothing
 */
void KiVgaUpdateCursor()
{
	outportb(0x3D4, 14);
	outportb(0x3D5, (y * 80 + x) >> 8);
	outportb(0x3D4, 15);
	outportb(0x3D5, (y * 80 + x));
	
	return;
}

/*! \fn void KsVideo()
 * \brief initializes the text-mode video driver
 * \return Nothing
 * \note will clear the screen, but only if not in debug mode
 */
void KsVideo()
{
	vgamem = (unsigned char *)0xB8000;
	if(!debug) clrscr();
	
	return;
}

/*! \fn void KiVgaSetTextColor(unsigned char forecolor, unsigned char backcolor)
 * \brief sets the foreground and background colors
 * \param forecolor Specifies the foreground color
 * \param backcolor Specifies the background color
 * \return Nothing
 * \note Text:	Sets the colors starting at the current cursor position
 * \note GUI:	Does nothing.
 */
void KiVgaSetTextColor(unsigned char forecolor, unsigned char backcolor)
{
//	if(GUIrunning == TRUE) return;
	
	color = (backcolor << 4) | (forecolor & 0x0F);
	
	return;
}

/*! \fn void putch(unsigned char c)
 * \brief Puts a character on the screen
 * \param c Specifies the character to print
 * \return Nothing
 * \note Text:	Prints the character at the cursors current location
 * \note GUI:	? - not sure yet (for now, does nothing.)
 */
void putch(unsigned char c)
{
	unsigned char *where;
	
	//if(GUIrunning == TRUE) return;
	
	where = (unsigned char *)(0xB8000 + (y * 80 + x) * 2);
	
	if(c >= ' ')
	{
		*where++ = c;
		*where++ = 0x07;
		x++;
		if (x == 80) { ++y; x = 0; }
	} else {
		switch(c)
		{
			case 0x08:
				memsetw(where - 1, (0x20 | (color << 8)), 1);
				x -= 1;
				break;
			case 0x09:
				memsetw(where, (0x20 | (color << 8)), 8);
				x = (x + 8) & ~(8 - 1);
				break;
			case '\r':
				x = 0;
				break;
			case '\n':
				x = 0;
				y++;
				break;
			default:
				break;
		}
	}
	if (y == 25) {
		memcpy((unsigned char *)(0xB8000), (unsigned char *)(0xB8000 + 160), (80 * 24 * 2));
		memsetw((unsigned char *)(0xB8000 + (160 * 24)), (0x20 | (color << 8)), 80);
		--y;
	}
	
	KiVgaUpdateCursor();
	
	return;
}

/*! \fn void putbuffer(char *buf, size_t len)
 * \brief Puts the content of a buffer on the screen
 * \param buf pointer to text to print
 * \param len length of buffer 
 * \return nothing
 */
void putbuffer(char *buf, size_t len)
{
	unsigned char *where;
	size_t i;

	for(i=0; i<len; i++) {
		where = (unsigned char *)(0xB8000 + (y * 80 + x) * 2);
		if(buf[i] >= ' ')
		{
			*where++ = buf[i];
			*where++ = 0x07;
			x++;
		} else {
			switch(buf[i])
			{
			case 0x08:
				memsetw(where - 1, (0x20 | (color << 8)), 1);
				x -= 1;
				break;
			case 0x09:
				memsetw(where, (0x20 | (color << 8)), 8);
				x = (x + 8) & ~(8 - 1);
				break;
			case '\r':
				x = 0;
				break;
			case '\n':
				x = 0;
				y++;
				break;
			default:
				break;
			}
		}
		if (y == 25) {
			memcpy((unsigned char *)(0xB8000), (unsigned char *)(0xB8000 + 160), (80 * 24 * 2));
			memsetw((unsigned char *)(0xB8000 + (160 * 24)), (0x20 | (color << 8)), 80);
			--y;
		}
	}
	KiVgaUpdateCursor();
	return;
}

static int printf_help(unsigned c, void **ptr)
{
	(void)ptr;
	putch(c);
	return 0;
}

/*! \fn void printf(const char *fmt, ...)
 * \brief print a formatted string
 * \param fmt format string
 * \param ... additional vars to print 
 * \return nothing
 */
void printf(const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	(void)do_printf(fmt, args, printf_help, NULL);
	va_end(args);
}

static int lprintf_help(unsigned c, void **ptr)
{
	(void)ptr;
	putch(c);
	return 0;
}

/*! \fn void lprintf(const char *fmt, ...)
 * \brief print a formatted string to the log file
 * \param fmt format string
 * \param ... additional vars to print 
 * \return nothing
 */
void lprintf(const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	if(!log) dprintf("lprintf: could not log message: ");
	if(log) (void)do_printf(fmt, args, lprintf_help, NULL);
	va_end(args);
}

static int dprintf_help(unsigned c, void **ptr)
{
	(void)ptr;
	putch(c);
	return 0;
}

/*! \fn void dprintf(const char *fmt, ...)
 * \brief print a formatted string if in debug mode
 * \param fmt format string
 * \param ... additional vars to print 
 * \return nothing
 * \note if AWOS is not running in debug mode, it will do nothing.
 */
void dprintf(const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	if(debug) (void)do_printf(fmt, args, dprintf_help, NULL);
	va_end(args);
}
